<!-- 数据列表组件 -->
<script setup lang="tsx">
import { SHORTCUT_PRIORITY, useShortcut } from '@/hooks/common/shortcut';
import {
    DataTableBaseColumn,
    DataTableFilterState,
    DataTableRowKey,
    PaginationProps
} from 'naive-ui';
import { computed, nextTick, onMounted, ref, watch } from 'vue';

defineOptions({ name: 'IDataTable' });

/** 组件属性接口 */
interface Props {
    /** table key */
    tableKey?: string;
    /** 渲染的 columns */
    columns?: any[];
    /** 渲染数据 */
    data?: any[];
    /** 是否加载中 */
    loading?: boolean;
    /** 分页配置 */
    pagination?: PaginationProps | null;
    /** 行样式 */
    rowClassName?: string;
    /** 挂钩的组件，触发 active 事件 */
    relations?: HTMLElement[] | HTMLInputElement[] | undefined[] | undefined;
    /** 树状结构数组使用 */
    tree?: boolean;
    /** 空列表展示的文本 */
    empty?: string;
    /** 汇总 */
    summary?: () => any;
    /** 是否为异步 */
    remote?: boolean;
    /** 动态行样式（支持数据校验） */
    dynamicRowClass?: (data: any) => string;
    /** 是否填充行索引 */
    rowFillIndex?: boolean;
    /** 优先级 */
    priority?: number;
}

/** 组件事件接口 */
interface Emits {
    (e: 'getActiveRow', row: any, cursor: number): void;
    (e: 'enter', row: any): void;
    (e: 'esc'): void;
    (e: 'into', el: any): void;
    (e: 'out', el: any, active: boolean): void;
    (e: 'selectBefore', rows: any[], meta: any): void;
    (e: 'selected', rowsKeys: any[], rows: any[], meta: any): void;
    (e: 'getSelectionType', type: 'true' | 'false' | 'mixed'): void;
    (e: 'click', row: any): void;
    (e: 'expand', row: any): void;
    (e: 'sorter', sorter: CommonType.OrderBy[]): void;
    (e: 'filters', filters: DataTableFilterState, sourceColumn: DataTableBaseColumn): void;
}

/** 组件事件 */
const emit = defineEmits<Emits>();

/** 组件属性 */
const props = withDefaults(defineProps<Props>(), {
    tableKey: 'data-table',
    columns: () => [],
    data: () => [],
    loading: false,
    pagination: undefined,
    rowClassName: 'data-table-row',
    relations: () => [],
    tree: false,
    empty: '',
    summary: undefined,
    remote: true,
    dynamicRowClass: () => '',
    rowFillIndex: true,
    priority: SHORTCUT_PRIORITY.PAGE
});

/** 数据表格 Ref */
const dataTable = ref<any>();
/** 当前行索引 */
const curRowIndex = ref<number>(0);
/** 选中项 */
const checkedRowKeys = ref<DataTableRowKey[]>([]);
/** 排序字段 */
const sortedColumns = ref<CommonType.Sorter[]>([]);
/** 真实的分页 */
const realPagination = computed(() => props.pagination || false);
/** 组件根元素引用 */
const tableContainer = ref<HTMLElement>();
/** 组件是否获得焦点 */
const isFocused = ref<boolean>(false);
/** 快捷键管理 */
const { bind, unbind } = useShortcut(props.priority);

/** 列表行元素 */
function tableRowProps(rowData: any, index: number): any {
    // 赋值 index
    rowData.index = index;
    // 赋值 active，根据当前索引判断是否激活
    rowData.active = index === curRowIndex.value;

    // 行钩子
    const rowProps = {
        'data-set-index': rowData.index,
        onClick: (ev: PointerEvent) => {
            const el = ev.currentTarget as HTMLElement;
            const newIndex = Number(el.getAttribute('data-set-index'));
            setActiveRow(newIndex);
        }
    } as any;

    // 动态样式
    if (props.dynamicRowClass) {
        const dynamicRowClass = props.dynamicRowClass(rowData);
        if (dynamicRowClass) {
            rowProps.class = dynamicRowClass;
        }
    }

    return rowProps;
}

/** 获取基本样式 */
function getRowClassName(row: any) {
    let className = 'data-table-row';

    if (props.rowClassName) {
        className += props.rowFillIndex
            ? ` ${props.rowClassName}-${row.index}`
            : ` ${props.rowClassName}`;
    }

    // 添加激活状态样式
    if (row.index === curRowIndex.value) {
        className += ' row-active';
    }

    return className;
}

/** 处理选中 */
function checkedHandler(rowKeys: DataTableRowKey[], rows: any[], meta: any) {
    checkedRowKeys.value = rowKeys;

    emit('selected', rowKeys, rows, meta);
}

/** 处理排序 */
function sorterHandler(sorter: CommonType.Sorter) {
    let exists = false;
    sortedColumns.value.forEach((item) => {
        // 命中更新
        if (item.columnKey === sorter.columnKey) {
            exists = true;

            // 首次 sorter = false 这时候切换为 descend
            if (!item.sorter) {
                item.order = 'descend';
                item.sorter = true;
            } else if (item.order === 'descend') {
                // 降序切换为升序
                item.order = 'ascend';
            } else if (item.order === 'ascend') {
                // 升序切换为无序
                item.sorter = false;
            }
        }
    });

    if (!exists) {
        sortedColumns.value.push(sorter);
    }

    emit(
        'sorter',
        sortedColumns.value
            .filter((item) => item.sorter)
            .map((item) => {
                return {
                    column: item.columnKey,
                    desc: item.order === 'descend'
                };
            })
    );
}

/** 官方过滤组件操作 */
function filtersHandler(filters: DataTableFilterState, sourceColumn: DataTableBaseColumn) {
    emit('filters', filters, sourceColumn);
}

/** 设置激活行 */
function setActiveRow(index: number) {
    if (index < 0 || index >= props.data.length) return;

    curRowIndex.value = index;
    const activeRowData = props.data[index];
    emit('getActiveRow', activeRowData, index);
}

/** 上一行 */
function moveToPreviousRow() {
    if (curRowIndex.value > 0) {
        setActiveRow(curRowIndex.value - 1);
    }
}

/** 下一行 */
function moveToNextRow() {
    if (curRowIndex.value < props.data.length - 1) {
        setActiveRow(curRowIndex.value + 1);
    }
}

/** 处理组件获得焦点 */
function handleFocusIn() {
    if (!isFocused.value) {
        isFocused.value = true;
        emit('into', tableContainer.value);

        // 绑定快捷键
        bind('ArrowUp', moveToPreviousRow);
        bind('ArrowDown', moveToNextRow);
    }
}

/** 处理组件失去焦点 */
function handleFocusOut(event: FocusEvent) {
    // 检查焦点是否移动到组件外部
    const relatedTarget = event.relatedTarget as HTMLElement;
    if (!tableContainer.value?.contains(relatedTarget)) {
        isFocused.value = false;
        emit('out', tableContainer.value, false);

        // 解绑快捷键
        unbind('ArrowUp');
        unbind('ArrowDown');
    }
}

/** 初始化数据 */
function initializeData() {
    if (props.data.length > 0) {
        // 默认激活第一行
        nextTick(() => {
            setActiveRow(0);
        });
    }
}

// 监听数据变化，重新初始化
watch(
    () => props.data,
    (newData) => {
        if (newData.length > 0) {
            // 重置到第一行
            curRowIndex.value = 0;
            initializeData();
        } else {
            curRowIndex.value = 0;
        }
    },
    { immediate: true }
);

// 组件挂载时初始化
onMounted(() => {
    initializeData();
});
</script>

<template>
    <div
        ref="tableContainer"
        tabindex="0"
        class="i-data-table-container"
        @focusin="handleFocusIn"
        @focusout="handleFocusOut"
    >
        <NDataTable
            ref="dataTable"
            v-model:checked-row-keys="checkedRowKeys"
            paginate-single-page
            summary-placement="top"
            :row-props="tableRowProps"
            :columns="columns"
            :data="data"
            :loading="loading"
            :remote="remote"
            :pagination="realPagination"
            :row-class-name="getRowClassName"
            :cascade="false"
            :summary="summary"
            @update:checked-row-keys="checkedHandler"
            @update:sorter="sorterHandler"
            @update:filters="filtersHandler"
        >
            <template #empty>
                <NEmpty v-if="props.empty" :description="props.empty"></NEmpty>
            </template>
        </NDataTable>
    </div>
</template>

<style scoped>
.i-data-table-container {
    outline: none;
}

.i-data-table-container:focus-within :deep(.row-active) {
    background-color: var(--n-td-color-hover);
    position: relative;
}
</style>
